<?php

1->初识RPC
   -) '介绍'
                         RPC 远程方法调用 (Remote Procedure Call)
                                            |
                            +------------------------------+
                            |                              |
                         服务治理                         RPC协议
                    (分布式环境注册中心)           (方法寻址/对象序列化/反序列化)

   -) '服务请求比对'
       Eureka(HTTP): 无论是通过'applicationName + 接口路径'还是'FeignClient'发起的调用, 总之我们一定要
       给定注册的'服务名 + 方法名称 + url后面的路径'.

       Dubbo(RPC): 不需要那些, 调用其他就像调用'本地方法一样', 不用给出服务名和服务路径, 但是需要借助'反射获取当前调用服务的特征量',
       然后运用到自己的方法寻址逻辑上, 帮我们找到正确的服务提供方! 在这个过程中会进行数据的'序列化 / 反序列化'.

   -) 'RPC VS REST HTTP'
       +==============+=================================+==================================+
       |              |               RPC               |               HTTP               |
       +==============+=================================+==================================+
       |    接口风格         面向执行过程(请求以动词命名)          面向所操作的资源(请求以名词命名)   |
       +--------------+---------------------------------+----------------------------------+
       |   应用层协议          RPC协议, 底层传输基于TCP          超文本传输协议, 底层传输基于TCP    |
       +--------------+---------------------------------+----------------------------------+
       |  变成友好程度         配置简单高效, 接口拿来就用          配置繁琐, 资源定位, GET/POST...  |
       +--------------+---------------------------------+----------------------------------+
       |    传输效率             应用gzip等压缩技术          HTTP携带的信息臃肿,报文中有效信息占比少 |
       +--------------+---------------------------------+----------------------------------+
       |  框架实现难度        难难难! 但和我们使用者没有关系                    简单               |
       +--------------+---------------------------------+----------------------------------+

       那么问题来了...
       问: RPC那么好, 为啥还要使用 SpringCloud和Eureka...

       答: RPC是好, 但是再好也就是个服务治理框架, 能有SpringCloud全家桶牛吗...
       你有: 网关层gateway吗, 有分布式配置中心吗, 有Bus推送服务吗, 有Sleuth追踪链吗, 有Sentinel限流哨兵吗,

   -) '适用场景'
       SpringCloud: 搭建微服务应用的全链路系统.
       Dubbo: 已经有了应用, 只不过想替换服务治理框架.


2->Dubbo架构设计
   -) '大话RPC和Dubbo'
       Dubbo是阿里巴巴在2012年开源的分布式服务治理框架，不仅是阿里巴巴在开源领域最出名的项目，也应该算称得上是国内影响力最大的开源大作。可是大家有所不知，
       阿里内部实际上都在使用另一个RPC框架叫HSF，这里面的恩怨情仇会在最后一个小节介绍（Dubbo和HSF的开发团队是同一拨人马，造轮子狂热症患者）,
       Dubbo这些年浮浮沉沉，也曾经一度停止了更新，而随着18年Dubbo3.0的强势推进，相信这个大版本中Dubbo会充分借鉴HSF在超高并发场景下积累的经验。

       在服务治理领域的泰山北斗是RPC和HTTP两派，据我观察，国内公司更偏爱基于RPC的微服务解决方案，国外公司则是偏爱Spring Cloud全家桶，
       不光是在服务治理的技术选型上有这种国内、国外的差别，很多开源组件的选型上都有这种地域色彩（拿持久层框架来说，国内偏爱使用iBatis/MyBatis，国外更偏爱ORM框架）

       个人偏向来说是建议使用Spring Cloud，并不是说SC在服务治理领域做得比Dubbo更好，而是更看好SC的体系构建能力。毕竟不是所有公司都有阿里这样的技术背景，
       可以为了追求极致造各种轮子，大部分公司需要的其实是Spring Cloud这样的微服务全套解决方案。就像盖房子一样，SC作为一家供应商，
       提供了应有尽有的各种建筑原材料，不用再为每个环节去费心选材。

   -) 'Dubbo特征'
       Dubbo是一款'轻量级+高性能的RPC框架'，所谓天下武学殊途同归，Dubbo的很多理念和Spring Cloud中的组件都差不多。
       - [基于接口 + 动态代理的远程方法调用]
          Dubbo对开发者屏蔽了底层的调用细节，在实际代码中调用远程服务就像调用一个本地接口类一样方便。这个功能和Feign很类似，但是Dubbo用起来比Feign还要简单很多，
          Feign有时还要独立声明一个新接口，而Dubbo真的就是拿到公共接口类后直接就能用。

       - [负载均衡]
          Dubbo内置多种负载均衡策略，智能感知下游节点健康状况，显著减少调用延迟，提高系统吞吐量。这部分功能和Ribbon十分接近，
          但是Dubbo的负载均衡策略不如Ribbon的多，而且Dubbo的负载均衡能力只能供自己享用，而Ribbon可以赋能给Spring Cloud里的各个组件.

       - [集群容错]
          Dubbo提供了一个Cluster组件专门用来做集群容错，它其实并不是Hystrix这类降级组件，而是一种'异常重试'组件，后面我们也有一个专门的小节来介绍这部分的内容

       - [服务治理]
          支持多种注册中心服务，服务实例上下线实时感知。这里面的功能相信大家都能猜个八九不离十，就是服务注册、服务发现、服务下线之类的流程，
          这些功能和Eureka里的概念是一样一样的，只是实现方式却大有不同。比如Dubbo在服务下线后会主动将可用服务列表下发到各个服务节点，
          送货上门服务周到，而Eureka每次都等着服务节点自己上注册中心来拿数据，不给包邮的。

       除了上面几个核心特性以外，还有诸如运行期流量调度（灰度、路由规则等）和可视化服务治理（运维工具，数据统计）等功能，我们就不一一介绍了。

   -) 'Dubbo架构'
       ======  初始化
       ------  异步通知
       ——————  同步阻塞

              2. Subscribe          +-------------+         1. Register
       +===========================>|   Registry  |<===========================+
       |                            +-------------+                            |
       |                                  |                                    |
       |                                  |                                    |
       |                       3. Notify  |                                    |
       |        +----------+              |                              +----------+
       +======= |          |<-------------+                              | Provider |<========+
                | Consumer | ——————————————————————————————————————————> +----------+         | 0. Start
                |          |                   4. Invoke                 | Container|=========+
                +----+-----+                                             +-----+----+
                     |                                                         |
                     |                                                         |
                     |                         5. Count                        |
                     |                        +---------+                      |
                     +----------------------->| Monitor |<---------------------+
                                              +---------+
       Register: 注册中心
       Provider: 服务提供方
       Consumer: 向Provider发起远程调用的消费者
       Monitor:  监控中心, 用来统计服务调用的频率和响应时间
       Container: 运行服务的容器

   -) '流程说明'
       . start: 服务容器启动后初始化服务提供者.
       . Register: 服务提供者在启动的过程中，向注册中心发起注册，这个步骤和Eureka的流程很像.
       . Subscribe: 服务消费者在启动的同时，向'注册中心订阅'所需的服务。这一个流程就和Eureka就大不相同了，Eureka是Consumer主动到服务中心去拉取数据，
                    而Dubbo采用了一种'Pub/Sub模式'，也就是发布订阅模型.
       . Notify: 注册中心将Provier地址列表发送给消费者，对于服务下线之类的变更，注册中心会主动推送变更数据到Consumer(建立在长连接之上).
       . Invoke: 服务消费者发起远程调用，这个过程会使用'负载均衡算法'挑选目标服务器.
       . Count: Consumer和Provider每隔一段时间将统计信息发送到监控中心，平时这些信息就暂存于'内存'当中.

   -) 'Dubbo和Eureka中服务发现的不同'
       服务发现应该是Dubbo和Eureka最大的一个不同之处。
       - 'Dubbo'
          Dubbo里的注册中心、Provider和Consumer三者之间都是长连接，借助于Zookeeper的高吞吐量，实现基于服务端的服务发现机制。
          因此Dubbo利用'Zookeeper+发布订阅模型'可以很快将服务节点的上线和下线同步到Consumer集群。如果服务提供者宕机，
          那么注册中心的长连接会立马感知到这个事件，并且立即推送通知到消费者。

       - 'Eureka'
          在服务发现的做法上Dubbo和Eureka有很大的不同，Eureka使用客户端的服务发现机制，因此对服务列表的变动响应会稍慢，比如在某台机器下线以后，
          在一段时间内可能还会陆续有服务请求发过来，当然这些请求会收到Service Unavailable的异常，需要借助Ribbon或Hystrix实现重试或者降级措施。

       对于注册中心宕机的情况，Dubbo和Eureka的处理方式相同，这两个框架的服务节点'都在本地缓存了服务提供者的列表'，因此仍然可以发起调用，
       但服务提供者列表无法被更新，因此可能导致本地缓存的服务状态与实际情况有别.


3->Dubbo核心功能
   Dubbo是一个精耕服务治理领域的框架，秉承了阿里一贯的大而全风格，和Eureka相比复杂度有不小的提高，这里精选了Registry和Remoting两个核心模块，
   从功能层面做个简单的了解.
   -) 'Registry'
       Registry是Dubbo中负责服务注册的模块，也就是我们熟悉的注册中心，目前Dubbo支持'五种不同的注册中心'，其中登场率最高的就是Zookeeper了.
       Zookeeper是Apache Hadoop的子项目，它底层是一个树形的目录结构，同时支持基于发布订阅模型的变更推送，这个特性特别适合应用在注册中心服务上。
       Zookeeper在各个大厂都有广泛应用，其稳定性和性能已经在业界得到了充分证明。大家如果想进一步了解Zookeeper可以逛逛官方开源文档:
       https://zookeeper.apache.org/
       - '注册中心的运作方式'
          注册中心只承担服务治理相关的功能（注册、发现、心跳和下线等），它不承担消息的转发（这一点和Eureka一样），因此不用承担访问压力。
          和Eureka相比，Dubbo在服务治理领域走的完全是不同的风格路线.

       - 'Dubbo长连接'
          Eureka的注册中心在服务治理领域采用的是一种“佛系”的连接策略，比如服务发现和心跳检测，都是等待服务节点自己上报状态。
          而Dubbo相比较之下就是“事必躬亲”的管理策略，Dubbo的注册中心，服务提供者和服务消费者三者之间均为长连接，注册中心严密监测节点的状态变化。

          Dubbo的注册中心基于长连接感知服务提供者的状态，假如服务提供者宕机，注册中心将立即推送事件通知消费者。
          但是注册中心自身宕机并不会影响已经上线运行的服务提供者和消费者。

       - '服务剔除'
          当服务提供者出现异常情况的时候（比如电源又被挖掘机铲断了），注册中心利用长连接可以侦听到服务的不可用状态，进而在服务列表中删除服务提供者。

          我们以Zookeeper注册中心为例，Dubbo的服务剔除原理实际上是利用Zookeeper的临时节点和会话保持的功能。ZK中的数据是和一个会话绑定的，
          一旦会话异常ZK就会清除这个会话创建的临时节点和所有订阅该会话的Watcher。

          我们结合Dubbo的长连接和服务剔除机制，讲一讲ZK中的会话（Session）是怎么一回事儿。在ZK中当一个长连接建立起来后，会生成一个Session ID，
          这是一个'全局唯一的标识符'。在会话期间内ZK通过心跳感应连接状态，如果在会话过期时间内没有心跳发送过来，或者因为网络原因导致会话断掉，
          那么这个时候客户端就会重新选择新的ZK节点进行连接。因此，当会话过期时，Dubbo的服务提供者和消费者能自动恢复注册数据，重新订阅请求。

          Eureka则不同，Eureka后台有一个服务自保的机制，也就是说短期的网络波动导致大比例节点无法连接到注册中心的时候，Eureka会开启自保模式，
          在这个模式下服务剔除功能将被禁用。

       - '心跳检测'
          Dubbo同样也有心跳检测，它的目的是检测服务提供者和服务消费者之间的连接是否还处于可用状态。如果在默认60s没有消息进来的时候，
          会尝试发送一个心跳Request，如果连着180秒既没有消息也没心跳，那么则会关闭Channel，对于Consumer来说则需要重新连接一个新的Provider。

       - '监控中心'
          服务提供者向注册中心注册其提供的服务，并汇报调用时间到监控中心。服务消费者向注册中心获取服务提供者地址列表，并根据负载算法直接调用提供者，
          同时汇报调用时间到监控中心。

          监控中心是一个可选组件，可以根据自身项目需要决定是否接入。监控中心和各个节点之间并不是长连接，同时监控中心的宕机并不会影响任何服务调用，
          只不过可能会丢失一部分的监控数据而已。

   -) '学习Tips'
       我们读书分为精读和泛读，学习也是一样，把精力花在重点知识的学习上。在整个微服务的大纲中，我们始终是围绕Spring Cloud作为主线，
       希望通过对SC核心组件的学习，能够将其应用到我们工作中的项目里，夯实微服务架构体系的实战应用，提升我们的架构能力。而Dubbo更多的是作为一种对照，
       让大家体会Dubbo和Eureka两种不同的服务治理理念，因此在Dubbo章节不会过于深入探讨某些底层技术。



4->Dubbo注册中心
   -) '注册中心种类'
       . Multicast ---> 一般本地测试使用）
       . Zookeeper ---> 应用最广泛
       . Nacos
       . Redis     ---> key value 过期时间
       . Simple

   -) '基于zk的服务注册模式'
       zk: 一种'树结构'.

       [根节点]                                             Dubbo
                                                             |
                                               +----------------------------+
                                               |                            |
       [服务名]                             Service1                     Service2
                                               |                            |
                                  +-----------------------+       +-----------------------+
                                  |                       |       |                       |
       [类型]                  Provider                Consumer Provider                Consumer
                                  |                       |
                         +-----------------+     +-----------------+
                         |                 |     |                 |
       [URL]        10.0.1.3:8080     10.0.1.4:8080...          10.10.34.2
                         |                 |     |                 |
                         +-----------------+     +-----------------+
                                   |                      |
                               服务提供方  <--负载均衡--- 服务调用方



5->使用————提供者
   -) '前置准备'
       . 下载并启动 (详见[附录-zookee.txt])
       . 此时服务端注册中心已就位 (服务端)

   -) 'pom'
       <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>

            <!-- dubbo依赖 -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.7.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-zookeeper</artifactId>
                <version>2.7.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>2.7.3</version>
            </dependency>

            <dependency>
                <groupId>${project.groupId}</groupId>
                <artifactId>dubbo-api</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>

       注意: 如果存在pojo模块的话, 需要实现序列化接口!

   -) '注解'
       @SpringBootApplication
       @EnableDubbo
       public class DubboClientApplication {
       }

       @Service
       public class ServierImpl implement IService {

       }

       注意:
       @EnableDubbo - 该注解是使用'apache.dubbo.config'包下的Dubbo!
       @Service - 该注解是使用'apache.dubbo.config'包下的Service!

   -) '配置文件'
       dubbo:
        application:
          name: dubbo-client
        registry:
          # 注册中心地址
          address: zookeeper://127.0.0.7:2181
          # 使用哪种类型作为注册中心
          protocol: zookeeper
          # 启动时检测是否存在【注册中心】，注册中心不存在就报错
          check: false
        metadata-report:
          address: zookeeper://127.0.0.7:2181
        # 协议配置
        protocol:
          # 指定协议类型
          name: dubbo
          # dubbo协议缺省端口为【20880】，rmi => 1099，http和hessian => 80
        monitor:
          # 用于配置连接监控中心相关信息，可选
          protocol: register
      server:
        port: 60031


6->Dubbo协议解析过程
   -) 'Dubbo底层协议'
       你以为Dubbo只有一个RPC协议吗？非也，既然是阿里巴巴出品的开源项目，那自然秉承了“包罗万象”的一贯传统。Dubbo的底层有支持多达9种通信协议，
       并且他们都有各自的适用场景。我们快速的一扫而过:
       . Dubbo本尊: 这也是官方首推的底层通信协议;
       . RMI: Java界的老法师可能会对RMI比较熟悉，没错，这个就是JDK中'java.rmi'包下的实现，底层采用阻塞式（同步传输）的短连接+JDK中标准的二进制序列化;
       . Hessian: 用于集成Hessian的服务，基于HTTP短连接，采用Servlet向外暴露服务;
       . HTTP: 最简单的HTTP表单的协议
               - 适用场景: 可以使用浏览器直接查看，适用于需要同时给后台应用程序以及浏览器提供服务的场景（很多其他协议无法通过浏览器直接发起调用）
       . WebService: 基于大名鼎鼎的Apache CXF，使用基于SOAP的序列化技术
                     - 适用场景: 比如公司内部的系统是由多种不同语言构成的，那么这个场景就比较适合采用WebService协议，
                                WebService通常使用在系统集成和跨语言调用场景中
       . REST: 基于标准的Java REST API(JAX-RS 2.0) 实现的REST风格调用，Dubbo花了大力气在这部分的实现上面，它的使用风格和原生Spring MVC类似。
       . 杂七杂八: Dubbo还支持基于Thrift，Memcached和Redis的协议，不过相信大家在职业生涯中应该是用不到的 ^ - ^

   -) 'Dubbo协议特性'
       ['项目']                   ['说明']
       连接个数                    单连接
       连接方式                    长连接
       传输协议                    TCP
       底层通信协议                 Netty异步NIO
       序列化方式                  Hessian2或Dubbo序列化

       在官方的测试报告中，由于Dubbo将底层的通信框架从mina换成了Netty，大大提升了稳定性，主要体现在JVM Old区对象的数量减少了很多，因此Full GC的触发频率大幅减少。
       tips:
       上面提到了JVM，所以这里多说两句。JVM的知识历来都是面试中的常见问题，大家在面试前不能光靠背题目来应付，
       面对有经验的面试官来说几句话就能被问出来。这里建议大家手把手去做一些JVM调优的测试，构造一些极端场景，再用批量测试数据数据跑一遍，
       看看这些参数的微妙作用，再把JVM的dump文件拿出来跑出分析结果。到这一步你基本就能吊打80%的面试官了。学知识光看书是没用的，必须动手去做。

   -) 'Dubbo协议适用场景'
       尺有所短寸有所长，Dubbo并不是万能药，我们在使用它之前务必要知道它的适用场景:
       适用场景: 传入传出参数数据包较小 (建议小于100K), 但是并发量高的场景。简单来说就是短平快，'QPS/TPS'高但是数据量小的情况.
       不适场景: 尽量不要用Dubbo协议传输大数据包（比如大文件、视频、超大字符串等），这类场景建议使用上面介绍过的其他协议栈.


7->Cluster组件和集群容错
   -) 'Dubbo在分布式环境要解决的问题'
       为了避免单点故障，我们会把服务部署在多个服务器上，组成一个集群。像淘系营销优惠这样超高QPS的服务，还会堆更多的服务器。对于服务消费者来说，
       面对集群中这么多的可用节点不知所措，摆在眼前的就有两个主要问题:
       . 寻址: 如何决定哪个服务提供者进行调用？
       . 异常处理: 调用失败后怎么办？本地重试还是抛出异常？还是换一个节点重试呢？

       对于Spring Cloud中的组件来说这都不是问题，直接集成Ribbon就好了，既有负载均衡还有重试。
       但是大阿里出品的Dubbo怎能食嗟来之食？吃口馒头赌口气，非得造个轮子出来！

   -) 'Cluster组件和集群容错'
       为了处理这些问题，Dubbo定义了集群接口'Cluster'以及'Cluster Invoker'。集群Cluster用途是将多个服务提供者合并为一个Cluster Invoker，
       并将这个Invoker暴露给服务消费者。这样一来，服务消费者只需通过这个 Invoker 进行远程调用即可，至于具体调用哪个服务提供者，
       以及调用失败后如何处理等问题，现在都交给集群模块去处理。

       集群模块是服务提供者和服务消费者的中间层，为服务消费者屏蔽了服务提供者的情况，这样服务消费者就可以专心处理远程调用相关事宜。
       比如: 发请求，接受服务提供者返回的数据等。

   -) '集群容错的工作流'
       我们先来熟悉一下集群容错的工作流程和核心组件:

       Failover -+
       Failfast -|                              list                 +----> Registry
       Failsafe -|------ Cluster -----------+---------> Directory ---+
       Failback -|          |               |               |        +----> Static
       Forking  -+    merge |               |               | List<Invoke>
                            |               |               |
                            |               |   route       |        +----> Script
              -------->  Invoker -----------+--------->   Route   ---+
                invoke                      |               |        +----> Condition
                                            |               | List<Invoke>
                                            |               |
                                            |   select      |           +----> Random
                                            |---------> LoadBalance ----+----> RoundRobin
                                            |               |           +----> LeastActive
                                            |               |
                                            |               | invoke
                                            |   invoke      |
                                            +--------->  Invoker
       . 消费者初始化阶段
         这个阶段发生在服务消费者初始化期间，就是图片最左上角的merge操作。在这一步中，Cluster组件为服务消费者创建Cluster Invoker实例，
         消费者通过这个Invoker实例发起远程调用。

       . 消费者发起远程调用
         以FailoverClusterInvoker为例，消费者发起远程调用主要分为三个步骤:
         - Directory获取服务提供者: 调用Directory服务获取所有Invoker，Directory的作用就如同它的字面意思一样，相当于是一个目录，
           大家可以把Directory理解为一个保存Invoker的数据结构。不仅如此，Directory还会探知注册中心的变动，并随之更新Invoker列表.

         - Router过滤: Directory在每次更新Invoker列表后，会调用Router的route方法将不符合路由规则的Invoker过滤掉.

         - LoadBalance负载均衡: 从Directory获取到Invoker列表后，通过LoadBalance策略从中选择一个Invoker
           然后将请求参数递交给这个Invoker发起真实调用.

   -) 'Cluster Invoker —— 干部大集合'
                                          +---------+
                                          | Cluster |
                                          +----+----+
                                               |
                      +------------+-----------+------------+------------+
                      |            |           |            |            |
                  Failover      Failfast    Failsafe     Failback     Forking

       我们先来理一理Cluster和Cluster Invoker之间的关系。前面我们提到过，Cluster只是用来创建Cluster Invoker给消费者进行调用的，
       所以Cluster本身的代码非常简单。我们以FailoverCluster为例，它只包含了短短几行代码:
       public class FailoverCluster implements Cluster {
           public static final String NAME = "failover";

           public FailoverCluster() {
           }

           public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
               return new FailoverClusterInvoker(directory);
           }
       }
       它仅仅就是创建一个FailoverClusterInvoker对象返回，没有其他逻辑。不单FailoverCluster如此，其他的几个Cluster也都是这副样子。
       接下来，我们就挨个看一看这五种Cluster Invoker都发挥了什么作用

       Dubbo提供了六种集群容错方案，我们今天主要介绍以下五种，还特意留了一个当做支线剧情，让同学们自己去阅读源码学习（BroadcastCluster）.
       1). [FailoverClusterInvoker] - 指定重试次数
            - 首先从Directory中获取重试次数.
            - 根据重试次数进行循环调用，每次重试都从负载均衡组件中获取一个Invoker。如果调用失败，则把异常记录下来然后进行重试，
              如果重试次数达到上限了，那么就直接抛出异常

              在负载均衡调用之前，FailoverClusterInvoker对Invoker的选择还动了一点小手脚。这里引入了一种叫'粘滞连接'的优化方案，
              这名词听着很稀奇，所谓粘滞连接是指让服务消费者尽可能的调用同一个服务提供者，除非该提供者挂了再进行切换。大家只要记住粘滞连接的含义，
              就能很顺利的理解这个方法的用途。

       2). [FailbackClusterInvoker] - 后台定时重试
            FailbackClusterInvoker会在调用失败后，返回一个空结果给服务提供者，并通过定时任务对失败的调用进行重传，因此特别适合执行某些'通知'类型的任务。

            大家在使用这个Invoker的时候要特别注意它的'定时任务重传'功能，因为大部分情况下我们并不期望Dubbo在后台偷偷摸摸发起重试，
            毕竟这时候消费者对这个事件是感知不到的。

            FailbackClusterInvoker的流程从前到后主要有三个步骤:
            - doInvoker: 发起首次远程调用，如果调用失败则添加调用信息到addFailed方法中，同时返回给消费者一个空的RpcResult对象.
            - addFailed: 创建定时任务，每隔5秒执行一次，调用retryFailed方法对失败任务进行重试。
                         这个定时任务只会创建一次，任务启动后新添加的失败请求依然会被重试
            - retryFailed: 失败重试的主要逻辑都在这里，它保存了一个失败者名单，挨个执行方法，如果执行成功则从名单中移除.
            FailbackClusterInvoker的后台重试列表只是保存在内存中的一个Map结构，一旦服务器重启之后就不会重试了。
            个人建议慎选这个策略，这种后台重试的机制是消费者无法感知并且不能控制的，可能会带来无法预知的结果，
            而且在某些极端场景下（比如服务雪崩）可能会增加系统的压力。

       3). [FailfastClusterInvoker] - 早死早超生
            这个方法比较省心，'只会进行一次调用，失败后立即抛出异常'。老师再也不用担心服务没有实现幂等性了!

       4). [FailsafeClusterInvoker] - 睁一只眼闭一只眼
            这个方法比上面那个更省心，'它也只会发起一次调用，但是失败后不会抛出异常'。而是打印一行错误日志，然后返回一个空的RpcResult对象。

       5). [ForkingClusterInvoker] - 百万雄师过大江 - 不推荐!!!
            ForkingClusterInvoker简直就像开启了狂暴模式，它会通过线程池开启多个线程，对服务提供者同时发起调用，任意一个线程返回成功就会立即结束方法。
            考虑到它这种百万雄师过大江的方式对服务器的负载能力简直是一种噩梦，因此它的适用场景'极度局限'，只能应用在某些对Timing要求极高的场景
            (最好是Read操作，即便是实现了幂等性的Write也不要使用这个策略，浪费资源).



8->使用————消费者
   pom / 配置文件 / 注解都和'提供者一样', 只有在实际使用注入'service'时, 代替'传统-@Autowired'!
   注意: @Reference 注是'apache.dubbo.config'包里的注解!

   @Reference(loadbalance = "roundRobin")
   private IDubboService dubboService;

   扩展: 此时如果将注册中心zk关闭, 那么接口间还是可以请求的!
        因为消费者在本地维护了一份静态路由表, 但是对后续'新增接口'无法请求! 因为没有注册中心了...


9->Dubbo_Admin (搭配vue的UI界面!)
   详见 - '附录-dubbo-admin.txt'.
   注意: 修改zookeeper的启动端口, 否则会和dubboAdmin产生冲突.


10->Dubbo的负载均衡
    -) '负载理念'
        Spring Cloud中的负载均衡组件Ribbon，尽管Dubbo使用的是自己的负载均衡方案 (阿里的风格就是绝不用别人的，只自己造轮子),
        但其实理念上都是大同小异。负载均衡无非就是将访问请求用某种方式尽量'平均'的分发到服务节点上，避免部分服务器因负载过高而挂掉.

    -) '低调的Dubbo负载 ———— 四件套'
        相比Ribbon负载均衡策略里的十八般兵器，Dubbo就显得低调的多了，它只提供了负载均衡四件套，让我们先来简单了解一下:
        +===============================+=======================================+
        |       RandomLoadBalance       |       基于'权重算法'的负载均衡策略        ||
        +===============================+=======================================+
        |     LeastActiceLoadBalance    |         基于'最少活跃数'调用数算法        |
        +-------------------------------+---------------------------------------+
        |   ConsistentHashLoadBalance   |             基于'Hash'一致性            |
        +-------------------------------+---------------------------------------+
        |     RoundRobinLoadBalance     |            基于'加权轮询'算法            |
        +-------------------------------+---------------------------------------+

    -) 'RandomLoadBalance'
        RandomLoadBalance是Dubbo的缺省实现，所谓权重算法，实际上是加权随机算法的意思，它的算法思想相当简单:
                                            +------------+
                                            |   总权重10  |
                                            +------------+

                服务器A (权重5)               服务器B (权重3)             服务器C (权重2)
                                    |                           |                         |
        0     1     2     3     4   |       5      6      7     |      8         9        |
                                    |                           |                         |

        假设我们有一组服务器分别是A，B，C，他们对应的权重为A=5，B=3，C=2，权重总和为10。现在把这些权重值平铺在一维坐标值上,
        [0, 5) 区间属于服务器 A，[5, 8) 区间属于服务器 B，[8, 10) 区间属于服务器 C.

        接下来通过随机数生成器生成一个范围在 [0, 10) 之间的随机数，然后计算这个随机数会落到哪个区间上。比如数字3会落到服务器 A 对应的区间上，
        此时返回服务器 A 即可。权重越大的机器，在坐标轴上对应的区间范围就越大，因此随机数生成器生成的数字就会有更大的概率落到此区间内。

        只要随机数生成器产生的随机数分布性很好，在经过多次选择后，每个服务器被选中的次数比例接近其权重比例。
        比如，经过一万次选择后，服务器 A 被选中的次数大约为5000次，服务器 B 被选中的次数约为3000次，服务器 C 被选中的次数约为2000次。

    -) 'LeastActiveLoadBalance'
        这个算法的思想就是“能者多劳”，它认为当前活跃调用数越小，表明该服务提供者效率越高，单位时间内可处理更多的请求，因此应优先将请求分配给该服务提供者。

        该算法给每个服务提供者设置一个“active”属性，初始值为0，每收到一个请求，活跃数加1，完成请求后则将活跃数减1。在服务运行一段时间后，
        性能好的服务提供者处理请求的速度更快，因此活跃数下降的也越快，此时这样的服务提供者能够优先获取到新的服务请求、这就是最小活跃数负载均衡算法的基本思想。

        当然这种算法也有不公平的地方，比如某台机器是扩容后新上线的机器，因此active的值是0，而这时Dubbo会认为这台机器的性能快如闪电，但其实这台机器的性能有可能慢如老狗。

        除了最小活跃数以外，LeastActiveLoadBalance 在实现上还引入了权重值。所以准确的来说，LeastActiveLoadBalance 是基于加权最小活跃数算法实现的。

        举个例子说明一下，在一个服务提供者集群中，有两个性能优异的服务提供者。某一时刻它们的活跃数相同，此时 Dubbo 会根据它们的权重去分配请求，
        权重越大，获取到新请求的概率就越大。如果两个服务提供者权重相同，此时随机选择一个即可.

    -) 'ConsistentHashLoadBalance'
        一致性Hash算法由麻省理工学院的Karger及其合作者于1997年提出的，算法提出之初是用于大规模缓存系统的负载均衡。在Dubbo中它的工作流程是这样的
        . 首先根据服务地址为服务节点生成一个Hash，并将这个Hash 投射到 [0, 232 - 1] 的圆环上;
        . 当有请求到来时，根据请求参数等维度的信息作为一个Key，生成一个 hash 值。然后查找第一个大于或等于该Hash值的服务节点，并将这个请求转发到该节点;
        . 如果当前节点挂了，则查找另一个大于其Hash值的缓存节点即可.

        4台机器均匀分布在圆环中，所有请求会访问第一个大于或等于自身Hash的节点。Server 3这台机器处于不可用的状态，因此所有请求继续向后寻址直到找到Server 4.

    -) 'RoundRobinLoadBalance'
        终于看到一个熟悉的面孔了，它和Ribbon的RoudRobinRule差不多。我们先来理解下什么是加权轮询。

        所谓轮询: 是指将请求轮流分配给每台服务器。比如说我们有3台机器A、B、C，当请求到来的时候我们从A开始依次派发，
                 第一个请求给A，第二个给B，依次类推，到最后一个节点C派发完之后再回到A重新开始.

        从上面的例子中可以看出，每台机器接到请求的概率是相等的，但是在实际应用中我们并不能保证每台机器的效率都一样，
        因此可能会出现某台Server性能特别慢导致无法消化请求的情况。因此我们需要对轮询过程进行加权，以调控每台服务器的负载。

        经过加权后，每台服务器能够得到的请求数比例，接近或等于他们的权重比。比如服务器 A、B、C 权重比为 5:2:1。那么在8次请求中，
        服务器 A 将收到其中的5次请求，服务器 B 会收到其中的2次请求，服务器 C 则收到其中的1次请求。



11->Dubbo————配置负载均衡
    Dubbo可以在类级别（@Service）和方法级别（@Resource）指定负载均衡策略，以方法级别为例，下面的代码配置了使用RoundRobin的负载均衡规则:

    @Reference(loadbalance = "roundrobin")
    private IDubboService dubboService;



12->HSF (High Speed Framework)
    -) 'Dubbo使用公司'
        大部分公司都是传统企业，放眼国内，除了阿里和滴滴以外没看到一二线的互联网公司，而且据我了解有几家叫得上名的互联网公司尽管在用Dubbo，
        但应用范围不大，并没有在公司上下游系统全面铺开。

        国内的情况大家心里有数了，然后我们放眼国外市场，算了还是不要放眼了，感觉压根没有哪家知名外企使用Dubbo的。

        说里面的原因可就多了。其实大部分一二线互联网公司技术实力都比较厉害，轮子也造的不少，这类大厂大多都有自己的一套服务治理框架，或者基于开源项目做了二次开发。
        Dubbo的应用主要还是集中在国内市场，在国外公司的应用非常少，国外大行其道的还是Spring Cloud，以及grpc等国外大厂研发的RPC框架。

    -) 'Dubbo接班人'
        真正在集团大行其道的RPC框架另有其人，那就是这一节的主角HSF (High Speed Framework)，我们可以叫它'好舒服'。

        说到HSF的应用范围，你可以认为是整个阿里系都在用它。HSF是由淘宝平台研发的，连包名都是”com.taobao“开头，大家不要以为它只在淘宝平台使用，
        HSF在整个淘系链路、天猫、集团中台服务以及各个独立事业部中已经全面铺开，用一统江湖形容绝不为过。在未来相当长的一段时间里，
        HSF还会是阿里系占绝对主导地位的RPC方案。

    -) '使用风格'
        <hsf:consumer
        	id="product"
        	interface="com.xm.training.ProductService"
        	version="1.0.0"
        	group="testHSFGroup"
        >
        上面这段是基于HSF标签的配置文件，创建了一个Consumer。这里面定义了接口类名，版本号和group三个信息，HSF 将根据 ['interface + version + group']
        查询并订阅所需服务。

        这里面的version其实是个蛮有用的字段，也会随着服务注册同步到注册中心。我经常需要和共享事业部的中台团队做对接，他们在同一时间可能会对接来自于多个业务方的需求，
        但是日常环境就只有一套数据库（阿里系一般只有日常和生产两套数据库，预发环境则是直接连接生产库），所有开发人员都会在本地启动服务，
        然后通过配置不同的version来区别不同的代码分支。比如营销大中台给天猫项目暴露的接口版本是1.0.1.TM, 对接飞猪的是1.0.1.FZ，
        这样一来不同的业务方就可以在Consumer端指定一个version，HSF会保证Consumer的调用请求只会由对应版本的Producer来处理。

    -) '技术基本面'
        HSF的底层技术栈和Dubbo差别不大，也是采用Netty作为网络通信层，同时采用Hession（默认）或JDK标准序列化方式。
        . 注册中心: 不同于Eureka的服务发现方式（客户端主动获取），HSF中注册中心与服务节点建立长连接，并通过心跳检测的方式判断节点的运行状态，
                   在某个节点变为不可用的时候，配置服务器会更新服务提供者列表（剔除不可用节点），并且主动推送到服务调用者.
                   - 在生产环境中不会使用一台配置服务器连接所有节点，而是会搭建多个配置服务器做负载均衡，保证变更可以尽快推送到所有节点.

        . 地址服务器: 为服务提供者和服务调用者提供所有注册中心的地址，所以每台机器在向注册中心发起服务注册之前，先要访问地址服务器获取注册中心的地址.

        除此之外，通常HSF还会集成Diamond（配置管理）之类的中间件，因此在部署结构上，确实比开源的Dubbo组件要复杂很多。
        所以说，HSF的高性能和高可用，很大程度上是建立在阿里系强大的运维能力之上的，如果这套架构用在其他公司自己运维的话，未必可以玩转这套复杂玩法。

        目前HSF可以通过阿里云的企业级分布式应用服务EDAS来提供接入服务，EDAS可以同时支持HSF、Dubbo和Spring Cloud三套微服务框架。借助云服务可以降低运维成本。

    -) '开源路漫漫其修远兮'
        淘系过去长期的实践经验已经说明了事实，在稳定性方面，HSF可谓是RPC框架的一枝独秀，可是既然HSF那么好，我何不放弃Dubbo转而使用HSF呢？
        大家可能也会感到纳闷，为什么阿里系内部都在使用HSF，而被Apache列为顶级项目的却是Dubbo呢？这就要说到开源项目的一些隐形门槛了。

        首先从实现的角度来说Dubbo更加轻量级，而HSF深度融入了整个淘系链路，因此也依赖了很多阿里系开源或暂未开源的内部项目。
        也就是说，如果你使用HSF的话，不可避免的要拖家带口的使用一些阿里系的技术。把HSF单独抽出来做开源化也是一件不小的工程，就拿Apache的顶级项目来说，
        就要经过提案、孵化和毕业三个阶段
        - 提案: 这个阶段还是遵循'导师制'，就是说需要委员会的三个大魔导师联合提名，成功后Apache会组建委员会为这个项目保驾护航.
        - 孵化: 孵化也不仅仅是开源项目代码这么简单，还包括开源社区的培养，布道师们要去开疆扩土，这不单是说项目写得好就可以的，
               还考验了项目团队或项目发起人的沟通和宣传能力
        - 毕业: 当孵化接近成熟了，并且Apache委员会的大部分导师都同意以后，项目就宣告毕业了，也就成为了Apach的正式开源项目.

    -) 'Spring Cloud、Dubbo、HSF三选一？'
        目前网络上关于HSF的资料少之又少，也没有详尽的文档，更谈不上构建学习体系。这就要吐槽阿里内部的文档建设工作了，
        集团里一众的中间件都是只管开发不管写文档的状态。开发迭代速度那是真快，就如同那脱缰的野狗一样，但是文档要么年久失修要么压根就没得可修。
        每次碰到问题在内网根本搜不到对应的文档，只能辗转找到负责开发的团队，或者请部门里的老法师给开个眼，非常耽误时间和精力。

        Dubbo因为有Apache在开源项目孵化阶段保驾护航，所以文档化做的非常好。但那些淘系内部的各类王牌中间件就是另一番情况了，文档建设工作非常滞后。
        其实这也是大部分技术人员的天性，只顾闷头做出牛B的项目，顾不上写文档，所以别人还是不知道这个项目的牛B之处
        - 'Dubbo'
           比起HSF来说，Dubbo在文档建设上做的非常好，而且独立运维起来难度也不大。但是就像之前提到的，Dubbo只是一个RPC框架，
           只关注服务治理这个方面的东西。但是构建微服务远不止服务治理这一项内容，如果你想体验自己组装电脑的乐趣，
           可以用Dubbo再加上一大堆自选组件来构建你的微服务。但是如果你想直接买一个一体机，你可能更需要Spring Cloud这种全家桶。

        - 'Spring Cloud'
           和HSF以及Dubbo比较，社区支持、文档化和组件丰富程度都处于完全压倒性的优势，还想啥呢？I WANT YOU ^-^
